/* ========================= eCAL LICENSE =================================
 *
 * Copyright (C) 2016 - 2025 Continental Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ========================= eCAL LICENSE =================================
*/

/**
 * @file   msg/protobuf/client_protobuf_utils.h
 * @brief  eCAL client protobuf service utility functions
**/

#pragma once

#include <ecal/service/client.h>
#include <ecal/msg/protobuf/ecal_proto_dyn.h>

#include <google/protobuf/service.h>
#include <google/protobuf/descriptor.h>

#include <stdexcept>
#include <string>

namespace eCAL
{
  namespace protobuf
  {
    /**
     * @brief Retrieves the full service name from the Protobuf descriptor of the given type T.
     *
     * The service type T is expected to be generated by Protobuf and have
     * a static method_descriptor or a protected GetDescriptor() method.
     * 
     * @tparam T  The Protobuf-generated service class.
     * 
     * @return    The full service name (e.g. "myproto.MyService").
     */
    template <typename T>
    std::string GetServiceNameFromDescriptor()
    {
      struct U : T {};  // Temporary subclass to access protected GetDescriptor()
      U temp_instance;
      return temp_instance.GetDescriptor()->full_name();
    }

    /**
     * @brief Creates a ServiceMethodInformationSetT for all methods in service type T,
     *        so that eCAL knows how to serialize/deserialize them (proto).
     *
     * @tparam T  The Protobuf-generated service class.
     * @return    The set of method info for eCAL registration.
     */
    template <typename T>
    eCAL::ServiceMethodInformationSetT CreateServiceMethodInformationSet()
    {
      struct U : T {};  // Temporary subclass to access protected GetDescriptor()
      U temp_instance;
      const google::protobuf::ServiceDescriptor* service_descriptor = temp_instance.GetDescriptor();

      if (service_descriptor == nullptr)
      {
        throw std::runtime_error("Failed to retrieve service descriptor.");
      }

      ServiceMethodInformationSetT method_information_set;
      CProtoDynDecoder dyn_decoder;
      std::string error_s;

      for (int i = 0; i < service_descriptor->method_count(); ++i)
      {
        const google::protobuf::MethodDescriptor* method_descriptor = service_descriptor->method(i);
        const std::string& method_name = method_descriptor->name();

        const std::string& request_type_name = method_descriptor->input_type()->name();
        const std::string& response_type_name = method_descriptor->output_type()->name();

        std::string request_type_descriptor;
        std::string response_type_descriptor;

        dyn_decoder.GetServiceMessageDescFromType(service_descriptor, request_type_name, request_type_descriptor, error_s);
        dyn_decoder.GetServiceMessageDescFromType(service_descriptor, response_type_name, response_type_descriptor, error_s);

        method_information_set.emplace(SServiceMethodInformation{
          method_name,
          SDataTypeInformation{request_type_name,  "proto", request_type_descriptor},
          SDataTypeInformation{response_type_name, "proto", response_type_descriptor}
          });
      }

      return method_information_set;
    }

  } // namespace protobuf
} // namespace eCAL
